﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Streambolics.PatLite
{
    public class PhcMessageInterpreter : IMessageConsumer, IMessageProducer
    {
        #region System wide constants

        private static byte[] _AckMessage = new byte[] { 0x06 };
        private static byte[] _NackMessage = new byte[] { 0x15 };

        #endregion

        #region Instance variables

        private byte[] _PendingMessage = null;
        private PhcStatus _Target;
        private byte[] _Buffer = new byte[5];
        private int _BufferPos = -1;

        #endregion

        #region Constructors

        public PhcMessageInterpreter (PhcStatus aTarget)
        {
            _Target = aTarget;
        }

        #endregion

        #region Parsing tools

        private byte[] InterpretBuffer ()
        {
            Ternary TargetValue;

            if (_PendingMessage != null)
            {
                return _NackMessage;
            }

            if (_BufferPos == 5)
            {
                switch (_Buffer[2])
                {
                    case 0x30:
                        TargetValue = Ternary.False;
                        break;
                    case 0x31:
                        TargetValue = Ternary.True;
                        break;
                    default:
                        return _NackMessage;
                }

                ChangeStatus (_Buffer[3], 0x1, 0, TargetValue);
                ChangeStatus (_Buffer[3], 0x2, 1, TargetValue);
                ChangeStatus (_Buffer[3], 0x4, 2, TargetValue);
                ChangeStatus (_Buffer[3], 0x8, 3, TargetValue);
                ChangeStatus (_Buffer[4], 0x1, 4, TargetValue);
                ChangeStatus (_Buffer[4], 0x2, 5, TargetValue);
                ChangeStatus (_Buffer[4], 0x4, 6, TargetValue);
                ChangeStatus (_Buffer[4], 0x8, 7, TargetValue);
                return _AckMessage;
            }

            return _NackMessage;
        }

        private void ChangeStatus (byte aCommandByte, byte aBitMask, int aOutputPos, Ternary aTargetValue)
        {
            if ((aCommandByte & aBitMask) != 0)
            {
                _Target[aOutputPos] = aTargetValue;
            }
        }

        #endregion

        #region IMessageConsumer Members

        public void ConsumeMessage (byte[] aMessage)
        {
            foreach (byte b in aMessage)
            {
                switch (b)
                {
                    case 0x40:
                        _BufferPos = 0;
                        break;
                    case 0x21:
                        _PendingMessage = InterpretBuffer ();
                        break;
                    default:
                        if (_BufferPos >= 0 && _BufferPos < 5 && b >= 0x30 && b <= 0x3F)
                        {
                            _Buffer[_BufferPos++] = b;
                        }
                        else
                        {
                            _BufferPos = -1;
                        }
                        break;                          
                }
            }
        }

        #endregion

        #region IMessageProducer Members

        public byte[] ProduceMessage ()
        {
            byte[] m = _PendingMessage;
            _PendingMessage = null;
            return m;
        }

        public void PeerReset ()
        {
        }

        #endregion
    }
}
